package Pakke;

import java.util.ArrayList;
import java.util.Collections;

/**
 * FancyTimerLogikk.java
 * Klasse som håndterer dataflyt og databehandling mellom gui, logikk og io.
 * @author arni
 *
 */

public class FancyTimerLogikk {
    private FancyTimerIO fancyTimerIO; // Objekt med metoder for hente inn / lagre data
    private ArrayList<LTimerData> lTimerData; // Liste av alle mulige timere
    private ArrayList<LNotification> lVarsling; // Liste med alle mulige varslinger
    private ArrayList<LTimer> lTimer; // Liste av alle aktive timere
    private ArrayList<FancyTimerSoundThread> lFTST; // Liste av alle aktive lydvarslingstråder

    /**
     * Konstruktør.
     * Lager fancytTimerIO objekt.
     * Laster inn LTimerData og LNotifications.
     */
    public FancyTimerLogikk() {
        fancyTimerIO = new FancyTimerIO();
        lTimer = new ArrayList<LTimer>();
        // Prøv å les verdier lagret på disk
        try {
            lTimerData = fancyTimerIO.loadLTimerData();
            lVarsling = fancyTimerIO.loadLVarsling();
        } catch (Exception e) {
            // Last inn defaultverdier hvis noe gikk galt med lesing fra disk
            lTimerData = loadDefaultLTimerData();
            lVarsling = loadDefaultLVarsling();
        }
        saveAllData();
           lFTST = new ArrayList<FancyTimerSoundThread>();
    }

    /**
     * Metode som lagrer objektene lTimerData og lVarsling via IO-klassen.
     *
     * @return returns true if save was ok
     */

    public boolean saveAllData() {
        boolean b = false;
        boolean c = false;
        synchronized(lTimerData) {
            b = fancyTimerIO.saveLTimerData(lTimerData);
        }
        synchronized(lVarsling) {
            c = fancyTimerIO.saveLVarsling(lVarsling);
        }
        return (b && c);
    }

    /**
     * Lager og laster inne standard LTimerData hvis programmet ikke finner objektene på disk.
     * @return Array list defaultTimerData
     *
     */

    private ArrayList<LTimerData> loadDefaultLTimerData() {
        ArrayList<LTimerData> defaultLTimerData = new ArrayList<LTimerData>();
        ArrayList<LTimerAlternative> ltaAR = new ArrayList<LTimerAlternative>();
        ltaAR.add(new LTimerAlternative("Litt kokt", 180000)); // 3m 180s
        ltaAR.add(new LTimerAlternative("Middels kokt", 360000)); // 6m 300s
        ltaAR.add(new LTimerAlternative("Veldig kokt", 600000)); // 10m 600s
        defaultLTimerData.add(new LTimerData("Egg", "Default lyd", ltaAR, 1, true));
        ArrayList<LTimerAlternative> ltaAR2 = new ArrayList<LTimerAlternative>();
        ltaAR2.add(new LTimerAlternative("Orginale", 6000000)); // 600s = 10m
        ltaAR2.add(new LTimerAlternative("Grandiosa", 720000)); // 12m 720s
        ltaAR2.add(new LTimerAlternative("Big One", 1080000)); // 18m 1080s
        defaultLTimerData.add(new LTimerData("Pizza", "Default lyd", ltaAR2, 1, true));
        ArrayList<LTimerAlternative> ltaAR3 = new ArrayList<LTimerAlternative>();
        ltaAR3.add(new LTimerAlternative("Kokvask", 8200000)); // 135m 3600 3600 900 8200s
        ltaAR3.add(new LTimerAlternative("Finvask", 4500000)); // 75m 4500
        ltaAR3.add(new LTimerAlternative("Tørketrommel", 7200000)); // 120m  7200s
        defaultLTimerData.add(new LTimerData("Klesvask", "Default lyd", ltaAR3, 1, true));
        return defaultLTimerData;
    }

    /**
     * Lager og laster inn standard LNotification data hvis programmet ikke finner objektene på disk.
     * @return arraylist defaultLVarsling
     *
     */

    private ArrayList<LNotification> loadDefaultLVarsling() {
        ArrayList<LNotification> defaultLVarsling = new ArrayList<LNotification>();
        defaultLVarsling.add(new LNSound("Default lyd", "sound/moredots.mp3"));
        return defaultLVarsling;
    }

    /**
     * Oppretter en LTimer basert på index i LTimerData.
     * @param indexLTimerData
     * @return returnerer true hvis LTimer blir opprettet
     */

    public boolean createTimer(int indexLTimerData) {
        synchronized (lTimerData) {
            synchronized(lTimer) {
                // Sjekker at referansen i indexLnTimerData eksisterer
                if ((indexLTimerData < 0) ||(indexLTimerData >= lTimerData.size())) return false;
                LTimerData ltd = lTimerData.get(indexLTimerData);
                LTimerAlternative lta = ltd.getlTidsAlternativer().get(ltd.getDefaultTimerAlternative());
                long timeToAlarm = System.currentTimeMillis() + lta.getTimeMs();
               lTimer.add(new LTimer(ltd.getName().trim(), lta.getName().trim(), ltd.getNotification().trim(), timeToAlarm));
            }
        }
        return true;
    }

      /**
     * Oppretter en LTimer basert på index i LTimerData.
     * @param indexLTimerData
       * @param indexTimerAlternativ
       * @return returnerer true hvis LTimer blir opprettet
     */

    public boolean createTimer(int indexLTimerData, int indexTimerAlternativ) {
    synchronized (lTimerData) {
        synchronized(lTimer) {
            // Sjekker at referansen i indexLnTimerData eksisterer
            if ((indexLTimerData < 0) ||(indexLTimerData >= lTimerData.size())) return false;
            LTimerData ltd = lTimerData.get(indexLTimerData);
            LTimerAlternative lta = ltd.getlTidsAlternativer().get(indexTimerAlternativ);
            long timeToAlarm = System.currentTimeMillis() + lta.getTimeMs();
           lTimer.add(new LTimer(ltd.getName().trim(), lta.getName().trim(), ltd.getNotification().trim(), timeToAlarm));
        }
    }
    return true;
    }
      
      /**
      *
      * Opprett ny LTimerData
      * @param name Navn på timer
      * @param notification Navn på varsling til denne timeren
      * @param defaultTimerAlternative
      * @param lTimerAlternative ArrayList av timeralternativer
      * @param favourite Skal timeren være favoritt
      * @return returnerer sant hvis det går bra
      *
      * Sjekker først om navnet på timeren ikke er brukt fra før.
      *
      */

    public boolean createLTimerData(String name, String notification, int defaultTimerAlternative, ArrayList<LTimerAlternative> lTimerAlternative, boolean favourite ){
        if (name.equals("")) return false;
        if (notification.equals("")) return false;
        boolean notOk = true;
        if(lTimerAlternative.size() < defaultTimerAlternative) return false;
        synchronized(lVarsling) {
            for (LNotification ln: lVarsling) {
                if (ln.getName().equals(notification)) {
                    notOk = false;
                    break;
                }
            }
        }
        if (notOk) return false;
        synchronized(lTimerData) {
            for (LTimerData ltd: lTimerData) {
                if (ltd.getName().equals(name)) return false;
            }
            lTimerData.add(new LTimerData(name, notification, lTimerAlternative, defaultTimerAlternative, favourite));
        }
        return true;
    }

    /**
     * Legger til en LNotification i lnotification arrayen
     * @param lnot
     * @return
     */
    public boolean createLNotification(LNotification lnot) {
        synchronized(lVarsling) {
            for (LNotification ln: lVarsling) {
                if(ln.getName().equals(lnot.getName())) return false;
            }
            lVarsling.add(lnot);
        }
        return true;
    }

    /**
     * Utvider tiden på en timer.
     * @param timerToSnooze
     * @param timeToSnoozeMs
     * @return returnerer true hvis tallet er fint
     */
    public boolean snoozeTimer(int timerToSnooze, int timeToSnoozeMs) {
        synchronized(lTimer) {
            long timestamp = System.currentTimeMillis() + timeToSnoozeMs;
            lTimer.get(timerToSnooze).setNotificationTimeMs(timestamp);
        }
        return true;
    }

    /**
     * 
     * @return Todimensjonal stringtabell med timernavn og default nedtellingstid for den timeren
     */

    public String[][] getFavouriteList (){
        String[][] favouriteList = null;
        synchronized(lTimerData) {
        favouriteList = new String[getNumberOfFavourites()][3];
        int i = 0;
        
            for(LTimerData ltd: lTimerData) {
                if(ltd.getFavourite()) {
                    favouriteList[i][0] = ltd.getName();
                    favouriteList[i][1] = ltd.getlTidsAlternativer().get(ltd.getDefaultTimerAlternative()).getName();
                    favouriteList[i][2] = ltd.getlTidsAlternativer().get(ltd.getDefaultTimerAlternative()).getTimeString();
                    i++;
                }
             }
         }
        return favouriteList;
    }

    /**
     *
     * @return returnerer liste med navnene på alle timerdataelementene
     */

    public String[] getTimerList (){
        String[] favouriteList = null;
        synchronized(lTimerData) {
        favouriteList = new String[lTimerData.size()];
        int i = 0;

            for(LTimerData ltd: lTimerData) {
                    favouriteList[i] = ltd.getName();
                    i++;

             }
         }
        return favouriteList;
    }

    /**
     *
     * @return returnerer stringtabell med timernavn og tid igjen av timeren
     */
    public String[][] getTickingList () {
        String[][] tickingList = null;
        synchronized(lTimer){
            tickingList = new String[lTimer.size()][5];
            int i = 0;
            for (LTimer lt: lTimer) {
                tickingList[i][0] = lt.getName().trim();
                tickingList[i][1] = lt.getAlternativeName().trim();
                tickingList[i][2] = lt.getStringTimeLeft().trim();
                if (lt.isActive()) tickingList[i][3] = "1";
                else tickingList[i][3] = "";
                if (lt.isCheckedOut()) tickingList[i][4] = "1";
                else tickingList[i][4] = "";
                i++;
            }
        }
        return tickingList;
    }

    /**
     * 
     * @param timerID fra 0-x
     * @return 2d liste med alle navn og tid på alternativene for valg timer
     */

    public String[][] getAlternatives (int timerID) {
        String[][] alternatives = null;
        synchronized(lTimerData){
            LTimerData ltd = lTimerData.get(timerID);
            ArrayList<LTimerAlternative> lta = ltd.getlTidsAlternativer();

            alternatives = new String[lta.size()][2];
            int i = 0;
            for (LTimerAlternative lt: lta) {
                alternatives[i][0] = lt.getName().trim();
                alternatives[i][1] = lt.getTimeString().trim();
                i++;
            }
        }
        return alternatives;
    }

    /**
     *
     * @return returnerer antall favoritter som ligger i lTimerData
     */

    private int getNumberOfFavourites() {
        int i = 0;
        for(LTimerData ltd: lTimerData) {
            if(ltd.getFavourite()) i ++;
        }
        return i;
    }

    /**
     * Sletter timerData på gitt ID
     * @param timer
     * @return
     */
    public boolean deleteTimerData(int timer) {
        if (timer < 0) return false;
        if (lTimerData.size() == 1) return false;
        synchronized(lTimerData) {
            if(lTimerData.size() < timer ) return false;
            lTimerData.remove(timer);
            return true;
        }
    }

    /**
     * Sletter timeralternativ x for gitt timerdata.
     * @param timer
     * @param timerAlternativ
     * @return
     */
    private boolean deleteTimerAlternative(int timer, int timerAlternativ) {
        if (timer < 0) return false;
        if (timerAlternativ < 0) return false;
        synchronized(lTimerData) {
            if(lTimerData.size() < timer ) return false;
            if (timerAlternativ == lTimerData.get(timer).getDefaultTimerAlternative()) return false;
            if (lTimerData.get(timer).getlTidsAlternativer().size() < timerAlternativ) return false;
            lTimerData.get(timer).getlTidsAlternativer().remove(timerAlternativ);
            return true;
        }
    }

    /**
     * Gjemmer en timer fra nedtellingslista.
     * @param timer
     * @return true of successful
     */
    public boolean hideTimer(int timer) {
        if (timer < 0) return false;
        synchronized(lTimer) {
            if (timer > lTimer.size()) return false;
            lTimer.get(timer).setCheckedout(true);
            return true;
        }
     }
    
    /**
     * Setter default alternativ for gitt timer til alternativ aId.
     * @param tId
     * @param aId
     */
    public void setAlternative(int tId, int aId) {
        synchronized(lTimerData) {
            lTimerData.get(tId).setDefaultTimerAlternative(aId);
        }
       
    }
    
    /**
     * Returnerer en stringtabell med informasjon om timerdata med plass id
     * @param id
     * @return
     */
    public String[] getTimerInfo(int id) {
        synchronized(lTimerData) {
            LTimerData ltd = lTimerData.get(id);
            String[] info = new String[(ltd.getlTidsAlternativer().size()*2)+3];
            info[0] = ltd.getName().trim();
            info[1] = ltd.getNotification().trim();
            String favourite;
            if(ltd.getFavourite()) {
                favourite = "Favoritt";
            } else {
                favourite = "Ikke favoritt";
            }
            info[2] = favourite.trim();
            int i = 2;
            for(LTimerAlternative lta: ltd.getlTidsAlternativer()) {
                i++;
               info[i] = lta.getName().trim();
               String time = lta.getTimeString().trim();
               i++;
               info[i] = time.trim();
            }
             return info;
        }
    }


    /**
     * Setter en timer aktiv eller passiv.
     * @param i
     * @param b
     */
    void setActive(int i, boolean b) {
        synchronized(lTimer) {
            lTimer.get(i).setActive(b);
        }
    }

    /**
     * Returnerer en strintabell med detalsjert info om en aktivtimer.
     * @param i
     * @return
     */
    public String[] getDetailedTimerinfo(int i) {
        String[] s = new String[3];
        synchronized(lTimer) {
            s[0] = lTimer.get(i).getName();
            s[1] = lTimer.get(i).getAlternativeName();
            s[2] = lTimer.get(i).getNotification();
        }

       return s;
    }

    /**
     * Fyrer av en alarm for timer med id i.
     * @param i
     */
    public void startNotification(int i) {
        String nSt;
        synchronized(lTimer) { nSt = lTimer.get(i).getNotification().trim(); }

        int nID = findNotification(nSt);
        LNotification lNot = lVarsling.get(nID);

        if (lNot instanceof LNSound) {
            LNSound lnS = (LNSound)lNot;
            String path = lnS.getPath().trim();
            FancyTimerSound fts = new FancyTimerSound(path);
            FancyTimerSoundThread ftst = new FancyTimerSoundThread(fts, i);
            lFTST.add(ftst);
            ftst.start();
        }
        if (lNot instanceof LNMail) { 
            LNMail lnM = (LNMail)lNot;
            FancyTimerSendMail ftsm = new FancyTimerSendMail(lnM.getRecipient(), "Mail notification from FancyTimer", "Your timer: "+ lTimer.get(i).getName() + " just completed");
            Thread mailthread = new Thread(ftsm);
            mailthread.start();
        }

        if (lNot instanceof LNTwitter) {
            LNTwitter lNtw = (LNTwitter)lNot;
            FancyTimerSendMail ftsm = new FancyTimerSendMail("cz2zsf-0cwrqx@twittermail.com", "tomt", "FancyTweet: " + lTimer.get(i).getName() + " just finished! @" + lNtw.getTwitterUser());
            Thread mailthread = new Thread(ftsm);
            mailthread.start();
        }
        if (lNot instanceof LNSMS) { lNot = (LNSMS)lNot; }
      
      
    }

    /**
     * Stopper en lydtråd som hører til en gitt timer
     * @param id
     */
    public void stopNotification(int id) {
        for (FancyTimerSoundThread ftst: lFTST) {
            if (ftst.getThreadId()==id) { ftst.interrupt(); }
        }
    }

    private int findNotification(String nSt) {
    int i = 0;
    for (LNotification ln: lVarsling) {
        if (ln.getName().equals(nSt)) return i;
        i++;
    }
    return 0;
    }


    /**
     * Sletter en varsling.
     * @param id
     * @return
     */
    public boolean deleteNotification(int id) {
        synchronized(lVarsling) {
            if (lVarsling.get(id).isLnProtected()) return false;
            lVarsling.remove(id);
            return true;
        }
    }


    /**
     * Returnerer en strengtabell med alle varslingene.
     * @return
     */
    public String[] getNotificationList(){
        String[] varslingerList = null;
        synchronized(lVarsling) {
        varslingerList = new String[lVarsling.size()];
        int i = 0;

            for(LNotification ltd: lVarsling) {
                    varslingerList[i] = ltd.getName();
                    i++;

             }
         }
        return varslingerList;
        }

    /**
     * Returnerer detaljert info om varslingene.
     * @param valg
     * @return
     */
    public String[] getNotificationInfo(int valg) {
            String[] r = new String[3];
            synchronized(lVarsling) {
                LNotification ln = lVarsling.get(valg);
                r[0] = ln.getName().trim();
                if (ln instanceof LNMail) {
                    r[1] = "Mail";
                    LNMail lnm = (LNMail)ln;
                    r[2] = lnm.getRecipient().trim();
                }
                if (ln instanceof LNSound) {
                    r[1] = "Sound";
                    LNSound lns = (LNSound)ln;
                    r[2] = lns.getPath().trim();
                }
                if (ln instanceof LNTwitter) {
                    r[1] = "Twitter";
                    LNTwitter lnt = (LNTwitter)ln;
                    r[2] = lnt.getTwitterUser().trim();
                }
            }
            return r;
        }
    
    /**
     * Legger til timerdataobjekt.
     * @param ltd
     */
    void addTimerData(LTimerData ltd) {
        synchronized(lTimerData) {
            lTimerData.add(ltd);
            Collections.sort(lTimerData);
            saveAllData();
        }
    }

    /**
     * Legger til varslingsobjekt.
     * @param ln
     */
    void addNotification(LNotification ln) {
        synchronized(lVarsling) {
            lVarsling.add(ln);
            Collections.sort(lVarsling);
            saveAllData();
        }
    }

}